using System;
using System.IO.Ports;
using Gadgeteer.Modules;

/*
 * Original Gadgeteer framework Copyright (c) Microsoft Corporation. (http://gadgeteer.codeplex.com/)
 * 
 * Gadgeteer Light made by Stefan Thoolen (http://gadgeteerlight.codeplex.com/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace Gadgeteer.Interfaces
{
    /// <summary>Wrapper for a serial port</summary>
    public class Serial
    {
        /// <summary>The actual serial port</summary>
        private SerialPort _serialPort = null;

        /// <summary>When enabled, the LineReceivedEventHandler will work</summary>
        public bool AutoReadLineEnabled { get; set; }

        /// <summary>AutoReadLine buffer</summary>
        private string _Buffer = "";

        /// <summary>Line delimiter</summary>
        public string LineReceivedEventDelimiter
        {
            get { return this._LineReceivedEventDelimiter; }
            set
            {
                if (value == null || value.Length == 0) return;
                this._LineReceivedEventDelimiter = value;
            }
        }
        private string _LineReceivedEventDelimiter = "\n";

        /// <summary>Creates a new serial port</summary>
        /// <param name="socket">The socket on which the port must be created</param>
        /// <param name="baudRate">Baud rate</param>
        /// <param name="parity">Parity</param>
        /// <param name="stopBits">Stopbits</param>
        /// <param name="dataBits">Databits</param>
        /// <param name="hardwareFlowControlRequirement">Not in use</param>
        /// <param name="module">The module</param>
        public Serial(Socket socket, int baudRate, SerialParity parity, SerialStopBits stopBits, int dataBits, HardwareFlowControl hardwareFlowControlRequirement, Module module)
        {
            this._serialPort = new SerialPort(socket.SerialPortName, baudRate, (Parity)parity, dataBits, (StopBits)stopBits);
            this._serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
        }

        /// <summary>Appairently we received data</summary>
        /// <param name="sender">The SerialPort object</param>
        /// <param name="e">Event data</param>
        private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (AutoReadLineEnabled && this.LineReceived != null)
            {
                // Reads out the buffer
                byte[] Buffer = new byte[this._serialPort.BytesToRead];
                this._serialPort.Read(Buffer, 0, Buffer.Length);
                // Appends the buffer to the global buffer
                for (int i = 0; i < Buffer.Length; ++i)
                    this._Buffer += (char)Buffer[i];

                // Looks for a breakpoint
                int Pos = this._Buffer.IndexOf(this._LineReceivedEventDelimiter);
                if (Pos >= 0)
                {
                    string Return = this._Buffer.Substring(0, Pos);
                    this._Buffer = this._Buffer.Substring(Pos + this._LineReceivedEventDelimiter.Length);
                    this.LineReceived(this, Return);
                }

            } else  if (!AutoReadLineEnabled && this.DataReceived != null) {
                this.DataReceived(this, e.EventType);
            }
        }

        // Some methods are just passed through
        public void Open() { this._serialPort.Open(); }
        bool IsOpen { get { return this._serialPort.IsOpen; } }
        public void Close() { this._serialPort.Close(); }
        public void Write(byte[] data) { this._serialPort.Write(data, 0, data.Length); }
        public void Write(byte[] buffer, int offset, int count) { this._serialPort.Write(buffer, offset, count); }
        public int Read(byte[] buffer, int offset, int count) { return this._serialPort.Read(buffer, offset, count); }
        public void Flush() { this._serialPort.Flush(); }
        public void DiscardOutBuffer() { this._serialPort.DiscardOutBuffer(); }
        public void DiscardInBuffer() { this._serialPort.DiscardInBuffer(); }
        public int BytesToWrite { get { return this._serialPort.BytesToWrite; } }
        public int BytesToRead { get { return this._serialPort.BytesToRead; } }

        /// <summary>Writes a string</summary>
        /// <param name="text"The string to write</param>
        public void Write(string text)
        {
            // Quickly converts the string to a byte array
            char[] CharArray = text.ToCharArray();
            byte[] ByteArray = new byte[CharArray.Length];
            for (int i = 0; i < CharArray.Length; ++i)
                ByteArray[i] = (byte)CharArray[i];

            this.Write(ByteArray);
        }

        /// <summary>Writes a line of text</summary>
        /// <param name="text">The text</param>
        public void WriteLine(string text)
        {
            this.Write(text + this._LineReceivedEventDelimiter);
        }

        /// <summary>Parity options</summary>
        public enum SerialParity
        {
            Even = System.IO.Ports.Parity.Even,
            Odd = System.IO.Ports.Parity.Odd,
            Mark = System.IO.Ports.Parity.Mark,
            Space = System.IO.Ports.Parity.Space,
            None = System.IO.Ports.Parity.None
        }

        /// <summary>StopBits options</summary>
        public enum SerialStopBits
        {
            None = System.IO.Ports.StopBits.None,
            One = System.IO.Ports.StopBits.One,
            OnePointFive = System.IO.Ports.StopBits.OnePointFive,
            Two = System.IO.Ports.StopBits.Two
        }

        /// <summary>Flow control options</summary>
        public enum HardwareFlowControl
        {
            NotRequired,
            UseIfAvailable,
            Required
        }

        /// <summary>Triggered when data is received</summary>
        /// <param name="sender">The serial port</param>
        /// <param name="data">The data</param>
        public delegate void DataReceivedEventHandler(Serial sender, SerialData data);
        public event DataReceivedEventHandler DataReceived;

        /// <summary>A line of text has been received</summary>
        /// <param name="sender">The serial port</param>
        /// <param name="line">The data</param>
        public delegate void LineReceivedEventHandler(Serial sender, string line);
        public event LineReceivedEventHandler LineReceived;
    }
}
